'''
文件名:Base.py
作用:对selenium进行二次封装
'''

# 导包
import re, pywinauto
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time, xlrd, xlwt
from pynput import keyboard
from pynput.keyboard import Key
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

"""带配置参数浏览器"""
# def open_browser(browser='Chrome'):  # 不输入参数,默认打开谷歌浏览器
#     '''
#     通过浏览器名称,打开对应的浏览器
#     :param browser: 浏览器名称
#     :param kwargs: 浏览器参数
#     :return: driver,即webdriver.browser()
#     '''
#     opt = webdriver.ChromeOptions()
#     opt.add_argument('--user-data-dir=C:/Users/31787/AppData/Local/Google/Chrome/User Data')  # 读取本地chrome配置
#     if browser.capitalize() == 'Chrome':  # 输入内容自动转为首字母大写,符合条件就打开谷歌浏览器
#         return webdriver.Chrome(options=opt)  # 加载配置)
#     elif browser.capitalize() == 'Firefox':  # 输入内容自动转为首字母大写,符合条件就打开火狐浏览器
#         return webdriver.Firefox()
#     elif browser.capitalize() == 'Edge':  # 输入内容自动转为首字母大写,符合条件就打开Edge浏览器
#         return webdriver.Edge()
#     else:  # 输入内容不符合以上条件时,输出内容
#         print('请输入正确的浏览器名称,例如Chrome,Firefox,Edge')

"""不带配置参数浏览器"""


def open_browser(browser='Chrome'):  # 不输入参数,默认打开谷歌浏览器
    '''
    通过浏览器名称,打开对应的浏览器
    :param browser: 浏览器名称
    :param kwargs: 浏览器参数
    :return: driver,即webdriver.browser()
    '''
    if browser.capitalize() == 'Chrome':  # 输入内容自动转为首字母大写,符合条件就打开谷歌浏览器
        return webdriver.Chrome()
    elif browser.capitalize() == 'Firefox':  # 输入内容自动转为首字母大写,符合条件就打开火狐浏览器
        return webdriver.Firefox()
    elif browser.capitalize() == 'Edge':  # 输入内容自动转为首字母大写,符合条件就打开Edge浏览器
        return webdriver.Edge()
    else:  # 输入内容不符合以上条件时,输出内容
        print('请输入正确的浏览器名称,例如Chrome,Firefox,Edge')


"""获取cookies"""


def get_token():
    """获取COOKies，通过Cookies实现免登录会使用到这一方法"""
    driver = open_browser()
    driver.maximize_window()
    driver.get('https://music.163.com/')
    time.sleep(12)
    cookies = driver.get_cookies()

    workbook = xlwt.Workbook(encoding='utf-8')  # 创建一个workbook并设置编码
    worksheet = workbook.add_sheet('sheet1')  # 添加sheet
    worksheet.write(0, 0, 'name')  # 第一行第一列写入内容
    worksheet.write(0, 1, 'value')
    worksheet.write(0, 2, 'path')
    worksheet.write(0, 3, 'domain')
    worksheet.write(0, 4, 'secure')
    worksheet.write(0, 5, 'httpOnly')
    worksheet.write(0, 6, 'sameSite')
    for i in range(1, len(cookies) + 1):  # 1,2,3,4,5,6
        worksheet.write(i, 0, cookies[i - 1]['name'])
        worksheet.write(i, 1, cookies[i - 1]['value'])
        worksheet.write(i, 2, cookies[i - 1]['path'])
        worksheet.write(i, 3, cookies[i - 1]['domain'])
        worksheet.write(i, 4, cookies[i - 1]['secure'])
        worksheet.write(i, 5, cookies[i - 1]['httpOnly'])
        # worksheet.write(i, 6, cookies[i - 1]['sameSite'])
    workbook.save('../data/blogs_cookies.xls')  # 将以上内容保存到指定的文件中
    driver.quit()
    print('获取cook完成！')


class Base(object):
    def __init__(self, driver):  # 将打开浏览器的返回值driver,即webdriver.browser()传进来
        self.driver = driver

    """浏览器相关方法"""
    """进入网页"""

    def open_url(self, url):
        '''
        进入网页
        :param url: 网页域名
        :return:
        '''
        try:
            self.driver.get(url)
            self.driver.maximize_window()  # 浏览器窗口最大化
        except:
            print('浏览器打开失败,无法输入网址')

    """浏览器后退"""

    def back(self):
        '''浏览器后退'''
        self.driver.back()

    """关闭浏览器"""

    def close_broswer(self):
        '''
        关闭浏览器
        :return:
        '''
        try:
            self.driver.quit()
        except:
            print('浏览器打开失败,无法执行关闭浏览器操作')

    """刷新浏览器"""

    def refrsh(self):
        """刷新浏览器"""
        self.driver.refresh()

    """页面元素相关方法"""

    """通过value属性定位"""

    def select_by_value(self, locator, value):
        """通过value属性定位"""
        element = self._find_element(locator)
        Select(element).select_by_value(value)

    """通过文本值定位"""

    def select_by_text(self, locator, text):
        """通过文本值定位"""
        element = self._find_element(locator)
        Select(element).select_by_visible_text(text)

    """查找元素（单个）"""

    def _find_element(self, locator: tuple, timeout: int = 8):  # 类的私有方法
        '''
        找单个元素
        :param locator:元素定位器,数据类型是元组
        :param timeout:最大等待时间,默认为10s
        :return:如果元素存在,返回元素本身,反之返回False
        '''
        try:
            # 显式等待和EC模块联用,定位单个元素
            return WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator))
        except:
            print(f'元素{locator}没有找到')
            return False

    """查找元素（多个/复数）"""

    def _find_elements(self, locator: tuple, timeout: int = 10):  # 类的私有方法
        '''
        找元素的复数形式
        :param locator:元素定位器,数据类型是元组
        :param timeout:最大等待时间,默认为10s
        :return:如果元素存在,返回元素本身,反之返回False
        '''
        try:
            # 显式等待和EC模块联用,定位元素的复数形式
            return WebDriverWait(self.driver, timeout).until(EC.presence_of_all_elements_located(locator))
        except:
            print(f'元素{locator}没有找到')
            return False

    """清空输入框内容"""

    def clear_text(self, locator):
        """
        清空输入框内容
        :param locator: 定位器,元组
        :return:
        """
        try:
            element = self._find_element(locator)  # 定位元素
            return element.clear()  # 清空输入框内容
        except:
            print(f"元素{locator}没有找到,无法清空输入框内容")

    """输入文本内容"""

    def send_keys(self, locator, text):
        '''
        元素中输入内容
        :param locator:元素定位器,数据类型是元组
        :param text:输入的内容
        :return:
        '''
        try:
            element = self._find_element(locator)  # 定位元素
            element.clear()  # 清空输入框
            element.send_keys(text)  # 输入内容
            element.send_keys(Keys.ENTER)
        except:
            print(f'元素{locator}没有找到,无法输入内容')

    """点击"""

    def click(self, locator):
        '''
        点击元素
        :param locator:元素定位器,数据类型是元组
        :return:
        '''
        try:
            element = self._find_element(locator)  # 定位元素
            element.click()  # 点击元素
        except:
            print(f'元素{locator}没有找到,无法点击')

    """获取元素文本值"""

    def get_element_text(self, locator):
        '''
        获取元素文本值
        :param locator: 元素定位器,数据类型是元组
        :return:
        '''
        try:
            element = self._find_element(locator)  # 定位元素
            return element.text  # 获取元素文本值
        except:
            print(f'元素{locator}没有找到,无法获取文本值')

    """获取元素属性值"""

    def get_element_value(self, locator, tag):
        '''
        获取元素的value属性值
        :param locator: 元素定位器,数据类型是元组
        :param Tag: 需要获取值的标签名，如title/name/class
        :return:
        '''
        try:
            element = self._find_element(locator)  # 定位元素
            return element.get_attribute(tag)  # 获取元素value属性值
        except:
            print(f'元素{locator}没有找到,无法获取value属性值')

    """判断元素是否被选中"""

    def is_selected(self, locator):
        '''判断元素是否被选中，返回bool值'''
        ele = self._find_element(locator)
        r = ele.is_selected()
        return r

    """判断元素是否找到"""

    def is_element_exist(self, locator):
        '''是否找到'''
        try:
            self._find_element(locator)
            return True
        except:
            return False

    """判断元素是否存在"""

    def is_displayed(self, locator):
        """判断元素是否存在"""
        try:
            return self._find_element(locator).is_displayed()
        except:
            return False

    """下拉选择"""

    def select(self, locator, index):
        '''
        下拉框选项选取
        :param locator: 下拉框定位器,数据类型是元组
        :return:
        '''
        try:
            element = self._find_element(locator)  # 定位下拉框
            select = Select(element)  # 创建一个Select类
            select.select_by_index(index)  # 通过索引选择选项
            return select
        except:
            print(f'元素{locator}没有找到,无法进行选择')

    """页面相关方法"""
    """进入iframe"""

    def in_iframe(self, param):
        '''
        进入iframe
        :param param: ①有id/name属性时,param为id/name属性值;②没有时,自己加上locator,locator为元组;③索引值.索引从0开始
        :return:
        '''
        try:
            self.driver.switch_to.frame(param)  # 进入iframe
        except:
            print(f'{param}没有找到,无法进入iframe')

    """切换iframe"""

    def switch_iframe(self, id_index_locator):
        """切换iframe"""
        try:
            if isinstance(id_index_locator, int):
                self.driver.switch_to.frame(id_index_locator)
            elif isinstance(id_index_locator, str):
                self.driver.switch_to.frame(id_index_locator)
            elif isinstance(id_index_locator, tuple):
                ele = self._find_element(id_index_locator)
                self.driver.switch_to.frame(ele)
        except:
            self.log.info("iframe切换异常")

    """退出iframe"""

    def out_iframe(self):
        '''
        退出iframe
        :param driver: driver=open_browser()
        :return:
        '''
        self.driver.switch_to.default_content()  # 返回最外层

    """捕获弹窗"""

    def click_alert(self):
        """
        捕获弹窗,点击确定
        :return:
        """
        alert = self.driver.switch_to.alert  # 捕获弹窗
        alert.accept()  # 点击确定按钮

    """执行js代码"""

    def js_excute(self, js):
        """
        执行js代码
        :param js: 需要被执行的js语句
        :return:
        """
        self.driver.execute_script(js)

    """聚焦元素"""

    def js_focus_element(self, locator):
        """聚焦元素"""
        target = self._find_element(locator)
        self.driver.execute_script("arguments[0].scrollIntoView();", target)

    """滚到顶部"""

    def js_scroll_top(self):
        """滚到顶部"""
        js = "window.scrollTo(0,0)"
        self.driver.execute_script(js)

    """滚到底部"""

    def js_scroll_end(self, x=0):
        """滚到底部"""
        js = "window.scrollTo(%s, document.body.scrollHeight)" % x
        self.driver.execute_script(js)

    def select_by_index(self, locator, index=0):
        '''通过索引，index是索引第几个，从0开始，默认第一个'''
        # if not isinstance(locator, tuple):
        #     raise LocatorTypeError(self.log.info("参数类型错误"))
        element = self._find_element(locator)
        Select(element).select_by_index(index)

    """鼠标/键盘相关操作"""
    """向下滑动滚轮"""

    def roller(self, vertical: int, hor: int = 10):
        """
        滚轮向下滚动
        :param hor: 水平距离,默认为0
        :param vertical:垂直距离
        :return:
        """
        js_down = f'window.scrollTo({hor},{vertical});'  # js代码
        self.driver.execute_script(js_down)  # 执行js代码

    """右键"""

    def right_click(self, ele):  # 鼠标右击
        return ActionChains(self.driver).context_click(ele).perform()

    """双击"""

    def double_click(self, ele):  # 双击
        return ActionChains(self.driver).double_click(ele).perform()

    """拖拽"""

    def drag_and_drop(self, start_ele, end_ele):  # 鼠标拖放（start_path=鼠标拖动源元素， end_path=鼠标释放目标元素）
        return ActionChains(self.driver).drag_and_drop(start_ele, end_ele).perform()

    """悬停"""

    def move_to_element(self, ele):  # 鼠标悬停
        return ActionChains(self.driver).move_to_element(ele).perform()

    """数字字符串提取"""

    def re_float_one(self, str):
        '''
        提取字符串的数字,提取第一个出现的符合条件的浮点数,如余额99997157.10,可用积分为12.0,提取结果为99997157.10
        :param str:需要提出浮点数的字符串
        :return:
        '''
        li = re.findall('[0-9]+.[0-9]+', str)
        num1 = li[0]
        return num1

    def re_float_two(self, str):
        '''
        提取字符串的数字,提取第二个出现的符合条件的浮点数,如余额99997157.10,可用积分为12.0,提取结果为12.0
        :param str:需要提出浮点数的字符串
        :return:
        '''
        li = re.findall('[0-9]+.[0-9]+', str)
        num1 = li[1]
        return num1

    def re_int_one(self, str):
        """
        提取字符串的数字,提取第一个出现的符合条件的数字,如您当前的可用积分为:100002470 积分，本订单最多可以使用3000 积分,结果为100002470
        :param str:需要提出整数的字符串
        :return:
        """
        li = re.findall('[0-9]+', str)
        num1 = li[0]
        return num1

    def re_int_two(self, str):
        """
        提取字符串的数字,提取第二个出现的符合条件的数字,如您当前的可用积分为:100002470 积分，本订单最多可以使用3000 积分,结果为3000
        :param str:需要提出整数的字符串
        :return:
        """
        li = re.findall('[0-9]+', str)
        num1 = li[1]
        return num1

    """文件/图片上传"""

    def upload_WinFile1(self, locator, filepath):
        """
        上传文件/图片
        :param locator: 上传按钮定位器
        :param filepath: 文件路径
        :return:
        """
        self._find_element(locator).click()
        time.sleep(2)
        app = pywinauto.Desktop()  # 通过窗口打开
        win = app['打开']  # 通过弹框名称进入控件中
        win['Edit'].type_keys(filepath)  # 输入上传图片的地址
        win['Button'].click()  # 点击打开按钮

    def upload_WinFile2(self, locator, filepath):
        el = self._find_element(locator)
        el.send_keys(filepath)

    """登录"""
    """通过使用cookie登录"""

    def no_login_token(self):
        """通过获取cookie实现免登录"""
        """免登录第二步，通过前面第一步获取到的cookice实现免登陆，前提是第一步已经执行过"""
        get_token()  # """第一次执行时，需要执行这一行代码（获取cook），第二次就不需要重新登录获取cook了"""
        self.open_url('https://music.163.com/')
        workbook_open = xlrd.open_workbook('../data/blogs_cookies.xls')  # 使用xlrd创建一个工作薄对象
        sheet = workbook_open.sheet_by_name('sheet1')  # 根据工作表的名称创建表格对象
        cookie_list = []  # 创建一个空列表
        for row_num in range(1, sheet.nrows):
            cookie_dict = {}  # 创建一个空字典
            cookie_dict['name'] = sheet.cell_value(row_num, 0)
            cookie_dict['value'] = sheet.cell_value(row_num, 1)
            cookie_dict['path'] = sheet.cell_value(row_num, 2)
            cookie_dict['domain'] = sheet.cell_value(row_num, 3)
            cookie_dict['secure'] = bool(sheet.cell_value(row_num, 4))  # 注意因为这里的值是TRUE、FALSE是布尔类型,需要强转一下,否则会当成字符串
            cookie_dict['httpOnly'] = bool(sheet.cell_value(row_num, 5))
            # cookie_dict['sameSite'] = sheet.cell_value(row_num, 6)
            cookie_list.append(cookie_dict)  # 以字典形式装入列表,[{'name':'','value':'','path':''},{},{},{},{},{}]
        for cookie in cookie_list:
            self.driver.add_cookie(cookie)  # 再用for循环输出并add_cookie
        self.driver.refresh()  # 刷新浏览器即可免密登录
        print('免密登录成功！')
        time.sleep(3)

    """使用元素点击登录"""

    def login_element(self):
        '''
        使用举例:
        base=Base(open_browser())
        base.login_user()
        :return:
        '''
        self.open_url('http://mes.kopsoft.cn/sys/dict')  # 进入登录页面
        self._find_element((By.XPATH, '用户名的元素定位')).send_keys('用户名')  # 输入用户名
        self._find_element((By.XPATH, '密码的元素定位')).send_keys('密码')  # 输入密码
        self._find_element((By.XPATH, '登录按钮的元素定位')).click()  # 点击登录

    """windows认证弹框登录"""

    def login_windows(self):
        self.open_url("http://10.0.0.83:3080/page/home")
        time.sleep(2)  # 加等待时间  避免按键操作过早报错
        obj = keyboard.Controller()
        #  press 按下按键 release 释放按键
        obj.press(Key.shift)  # 切换英文输入法
        obj.release(Key.shift)
        obj.type(f"sharewinfo\yanglin")  # 账号

        obj.press(Key.tab)  # 切换输入框
        obj.release(Key.tab)
        time.sleep(2)
        obj.type('1qaz@WSX@WSX')  # 密码

        obj.press(Key.enter)  # 登录
        obj.release(Key.enter)
        time.sleep(2)


if __name__ == '__main__':
    get_token()
    pass
